%{
using namespace std;

#include "DTD.h"
#include "Choix.h"
#include "Sequence.h"
#include "ItemDTD.h"



void dtd_error(DTD** arbreDTD,char *msg);
int dtd_wrap(void);
int dtd_lex(void);

%}

%union { 
	char *s;
	DTD * dtd; 
	vector<DeclElement *> * vec_element;
	AttributDTD * attribut;  //un attribut de type <!ATTLIST nom_ele nom_att, type, valeur>
	DeclElement * element;  //element d'un DTD de type <!ELEMENT nom(contenu...)>
	ContentDTD *contentDTD; //contenu d'un element : sequence ou choix ou itemDTD
	vector<Sequence *>* vec_sequence;
	vector<ContentDTD *>* vec_contentDTD;
	Sequence * sequence; //contentDTD ayant 1-N fils separes par ','
	Choix * choix; //contentDTD ayant 2-N fils, separes par '|'
	ItemDTD * itemDTD; // contentDTD sans fils
	map<string,map<string,AttributDTD*> > * attlist;// string -> nom de l'attribut 
   }


%token ELEMENT ATTLIST CLOSE OPENPAR CLOSEPAR COMMA PIPE FIXED EMPTY ANY PCDATA AST QMARK PLUS CDATA 
%token <s> IDENT TOKENTYPE DECLARATION STRING

%type <s> card default_declaration
%type <dtd> dtd_document
%type <attribut> attribute
%type <contentDTD> item_card item  contentspec children
%type <element> declaration_element
%type <vec_contentDTD> liste_mixed liste_sequence liste_choix liste_choix_plus choix sequence



%parse-param {DTD ** arbreDTD}
%%

main
:dtd_document	{*arbreDTD = $1;} //renseigner le pt de l'expression
;	

attribute
: IDENT att_type default_declaration {$$=new AttributDTD($1,$3);} //un attribut procede un nom-> IDENT, un type->att_type et une valeur par defaut
|/*empty*/	{$$=new AttributDTD();} // un element peut avoir 0 attribut
;




att_type
: CDATA    
| TOKENTYPE	
| enumerate	
;

enumerate
: OPENPAR enum_list_plus CLOSEPAR	
;

enum_list_plus
: enum_list PIPE item_enum	
;

enum_list
: item_enum               	
| enum_list PIPE item_enum	
;

item_enum
: IDENT		
;

default_declaration
: DECLARATION 		{$$ = $1;}
| STRING     		{$$ = $1;}
| FIXED STRING 		{$$ = $2;}
;

item_card
: item card		{$$ = $1 ;ContentDTD * con = $1;con->setOccurrence($2);} //ContentDTD-> item , occurrence ->card
;

card
: PLUS		{$$ ="+";}
|QMARK		{$$ ="?";}
|AST		{$$ ="*";}
| /*vide*/	{$$ ="";}
;

item
:choix		{$$ = new Choix();$$->setFils($1);} 
|sequence   {$$ = new Sequence();$$->setFils($1);}	
|IDENT		{$$ = new ItemDTD(string($1));} //string -> nom de l'identifiant
;

choix
: OPENPAR liste_choix_plus CLOSEPAR	{$$ = $2;} // liste_choix_plus contient 2-n items de type choix encadre par ()
;

sequence
: OPENPAR liste_sequence CLOSEPAR	{$$ = $2;} //liste_sequence contient 1-n items de type sequence encadre par ()
;

liste_choix
: liste_choix PIPE item_card		{$$= $1; $$->insert($$->begin(),$3);} //ajouter le premier item de la liste dans liste_choix
| item_card		{$$=new vector<ContentDTD *>; $$->insert($$->begin(),$1);}
;

liste_choix_plus 
: item_card PIPE liste_choix	{$$= $3; $$->insert($$->begin(),$1);} //cette grammaire permet d'interdire la liste_choix d'avoir un seul fils
;

liste_sequence
:item_card COMMA liste_sequence	{$$= $3; $$->insert($$->begin(),$1);} 
|item_card		{$$=new vector<ContentDTD *>; $$->insert($$->begin(),$1);}
;

//fils du contenu
children 
:choix card	{$$=new Choix();$$->setFils($1);ContentDTD * con = $$;con->setOccurrence($2);}
|sequence card	{$$=new Sequence();$$->setFils($1);ContentDTD * con = $$;con->setOccurrence($2);}
;

//ex de la declaration de l'element <!ELEMENT, identifiant (contenu...) > 
declaration_element
: ELEMENT IDENT contentspec CLOSE	{$$ = new DeclElement();$$->setName(string($2));$$->setContentDTD($3);}
;

//une liste mixed est declare comme <!ELEMENT...(#PCDATA|choix1| |choix2...)* > sont fils ne sont que des choix
liste_mixed
:liste_mixed PIPE IDENT 	{$$=$1; ContentDTD* contDTD = new ItemDTD($3);$$->insert($$->begin(),contDTD);}
| PCDATA		{$$ = new vector<ContentDTD *>; }
;

contentspec	
:OPENPAR liste_mixed CLOSEPAR	{$$=new ContentDTD();$$->setType("Mixte_NONAST");$$->setFils($2);}//le contenu procede une liste mixed simple
|OPENPAR liste_mixed CLOSEPAR AST	{{$$=new ContentDTD();$$->setType("Mixte_AST");$$->setFils($2);}} //une liste mixed procedant une cardinalité "*"
|EMPTY	{$$=new ContentDTD();$$->setType(string("Empty"));}//le contenu est de type EMPTY, il n'a pas de fils
|ANY	{$$=new ContentDTD();$$->setType("Any");}
|children 	{$$=$1} // le contenu procede les fils de type contentDTD
;

 //un doc dtd contient la declaration des elements suivie par la declaration de ses attributs  
dtd_document:
dtd_document ATTLIST IDENT attribute CLOSE {$$->addAttToElement(string($3),$4->getName(),$4);} 
|dtd_document declaration_element {$$->addElement($2->getName(),$2);} 
|/* empty */ {$$ = new DTD();}
;



 
%%
int dtd_wrap(void)
{
  return 1;
}

void dtd_error(DTD ** arbreDTD, char *msg)
{
  fprintf(stderr, "%s\n", msg);
}


